/**
 *******************************************************************************
 * @FileName  : user_main.c
 * @Author    : GaoQiu
 * @CreateDate: 2020-05-18
 * @Copyright : Copyright(C) GaoQiu
 *              All Rights Reserved.
 *******************************************************************************
 *
 * The information contained herein is confidential and proprietary property of
 * GaoQiu and is available under the terms of Commercial License Agreement
 * between GaoQiu and the licensee in separate contract or the terms described
 * here-in.
 *
 * This heading MUST NOT be removed from this file.
 *
 * Licensees are granted free, non-transferable use of the information in this
 * file under Mutual Non-Disclosure Agreement. NO WARRENTY of ANY KIND is provided.
 *
 *******************************************************************************
 */
#include "user_main.h"
#include "utils/mdc.h"
#include "utils/pool_buf.h"
#include "utils/msg.h"
#include "utils/stimer.h"
#include "utils/math.h"
#include "utils/heap.h"
#include "utils/lg_mem.h"
#include "utils/sys_time.h"
#include "utils/pm.h"
#include "utils/os_support.h"

#include "stack/controller/ll_api.h"
#include "stack/controller/bb_api.h"
#include "stack/controller/bb.h"
#include "stack/hci_defs.h"

#include "stack/host/dm_api.h"
#include "stack/host/l2cap_api.h"
#include "stack/host/att_api.h"
#include "stack/host/smp_api.h"
#include "stack/host/host_cfg.h"

#include "profile/svc_handle.h"

#include "bsp/bsp.h"
#include "component/kv/kv_api.h"

#if CFG_SLEEP_CLOCK_SOURCE == 2 && CFG_PM_EN
#error !!!Please select sleep clock for PM.
#endif

/*!< Heap Size */
#define APP_HEAP_MEM_SIZE       (3 * 1024)
static uint32_t heap[APP_HEAP_MEM_SIZE/sizeof(uint32_t)] = {0};

/*!< PM Enable/Disable */
#define APP_PM_EN               CFG_PM_EN
#define APP_ADV_CURRENT_TEST    0

/*!< LED blink period Unit:ms */
#define APP_LED_EN              !APP_PM_EN
#define APP_LED_BLINK_PERIOD    1000
#define APP_KEY_EN              1

/*!< Notify period Unit:ms */
#define APP_NOTIFY_EN           0
#define APP_NOTIFY_INTERVAL_MS  1000

/*!< Enable MAC Address filter */
#define APP_MAC_FILTER_EN       0
#define APP_FILTER_MAC          0x66, 0x66, 0x66, 0x66, 0x66, 0x66
#define APP_FILTER_LEN          4

/*! Connection parameter configuration */
#define APP_MAX_MST_NUM         0
#define APP_MAX_SLV_NUM         1
#define APP_MAX_CONN_NUM        (APP_MAX_MST_NUM + APP_MAX_SLV_NUM)

#define APP_ADV_EVT_TYPE        DM_ADV_CONN_UNDIRECT
#define APP_ADV_INTR_MIN        ADV_INTR_30_MS
#define APP_ADV_INTR_MAX        ADV_INTR_35_MS 
#define APP_ADV_CHN             DM_ADV_CHAN_ALL
#define APP_ADV_FILT            DM_ADV_FILTER_NONE
#define APP_ADV_LOCAL_ADDR_TYPE DM_ADDR_PUBLIC//DM_ADDR_RANDOM//DM_ADDR_PUBLIC
#define APP_ADV_PEER_ADDR_TYPE  DM_ADDR_PUBLIC
#define APP_ADV_PEER_ADDR       NULL

/*!< Sleep Clock Source Selected */
#define APP_SLP_CLK_SRC         CFG_SLEEP_CLOCK_SOURCE
#define APP_SCA_PPM             (APP_SLP_CLK_SRC ? 50:1000)

/*!< MCU Power supply mode. */
#define APP_MCU_PWR_SUP_MODE    CFG_DCDC_ENABLE

/* ACL Size Setting. */
#ifdef IP_108
#define APP_ACL_SIZE            27
#define APP_LL_ENC_TIME_US      100
#elif defined(IP_107)
#define APP_ACL_SIZE            27
#define APP_LL_ENC_TIME_US      80
#endif


/*! Link layer config. */
const LlCfg_t llCfg = {
	.versNr    = LL_VER_BT_CORE_SPEC,   /*!< BLE Core version.           */
	.compId    = LL_COMPANY_ID,         /*!< Company ID.                 */
	.subVersNr = LL_SUB_VERSION_NUM,    /*!< Implementation version.     */

	.wlNum     = 4,                     /*!< White-list size.            */
	.rlNum     = 0,                     /*!< Resolve list size.          */

	/* Extented adv/scan */
	.numAdvSet         = 0,
	.extAdvDataLen     = 0,
	.extScanRspDataLen = 0,
	.advMargin         = 0,              /*!< unit:ms */

	.maxMst    = APP_MAX_MST_NUM,        /*!< Max number of  master connection. */
	.maxSlv    = APP_MAX_SLV_NUM,        /*!< Max number of  slave connection. */
	.maxAclLen = APP_ACL_SIZE,           /*!< Length of ACL data.         */
	.numRxBufs = 16,                     /*!< Number of Rx buffer.        */
	.numTxBufs = 16,                     /*!< Number of Tx buffer.        */
	.localSCA  = APP_SCA_PPM,            /*!< Local sleep clock accuracy. */

	.mstMargin = 5,                      /*! Master connection margin. Unit: 0.625ms*/
	.numMoreDat= 7,                      /*! the max number of more data. Range: >1. */

	.sleepClockSrc = APP_SLP_CLK_SRC,    /*!< [Panchip] select sleep clock source. 0:32k RC, 1:32k XTL. */
	.bbSetupTime = 200,                  /*!< [Panchip] BB PHY setup time. Unit:us >160us */
	.llEncTime   = APP_LL_ENC_TIME_US,   /*!< [Panchip] BB CCM time. rang: 60us - 286us; Unit:us */
	.pwrSupMode  = APP_MCU_PWR_SUP_MODE, /*!< [PanChip] MCU power supply mode. 0:DCDC, 1:LDO */

	.authPyldTimeoutEn = false,
	.numMaxAnt = 0,
};

/******************************************************************************/
/* Connection Manager                                                         */
/******************************************************************************/
typedef struct{
	uint32_t        connStartTick;
	uint16_t        connHandle;
	uint8_t         role;
	uint8_t         inUse;
	uint8_t         isSmpCmpl;
	uint8_t         battNotifyEn;
	uint8_t         htsNotifyEn;

	MdcTimer_t      phyUpdTimer;
	MdcTimer_t      dleUpdTimer;
	MdcTimer_t      connUpdTimer;
}AppCCB_t;

/*! Application main control block. */
typedef struct{
	AppCCB_t        ccb[APP_MAX_CONN_NUM];
	MdcTimer_t      timer;
	MdcTimer_t      keyScanTimer;
	MdcTimer_t      notifyTimer;
	MdcHandlerId_t  handlerId;
}AppCb_t;

AppCb_t appCb;

AppCCB_t *APP_AllocCcb(uint16_t connHandle, uint8_t role)
{
	AppCCB_t *pCcb = appCb.ccb;

	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->inUse == false){
			pCcb->inUse = true;
			pCcb->connHandle = connHandle;
			pCcb->role = role;

			/* Start PHY Update Timer*/
			MDC_TimerConfigEx(&pCcb->phyUpdTimer, appCb.handlerId, APP_EVT_PHY_UPD, 0, connHandle);
			MDC_TimerStartMs(&pCcb->phyUpdTimer, 500);

			/* Start DLE Update Timer*/
			MDC_TimerConfigEx(&pCcb->dleUpdTimer, appCb.handlerId, APP_EVT_DLE_UPD, 0, connHandle);
			MDC_TimerStartMs(&pCcb->dleUpdTimer, 1000);

			/* Start Connection Update Timer*/
			MDC_TimerConfigEx(&pCcb->connUpdTimer, appCb.handlerId, APP_EVT_CONN_UPD, 0, connHandle);
			MDC_TimerStartMs(&pCcb->connUpdTimer, 4000);

			return pCcb;
		}
	}
	return NULL;
}

void APP_ReleaseCcbByHandle(uint16_t connHandle)
{
	AppCCB_t *pCcb = appCb.ccb;

	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->connHandle == connHandle && pCcb->inUse){
			pCcb->inUse = false;
			MDC_TimerStop(&pCcb->phyUpdTimer);
			MDC_TimerStop(&pCcb->dleUpdTimer);
			MDC_TimerStop(&pCcb->connUpdTimer);
			return;
		}
	}
}

AppCCB_t *APP_GetConnCb(uint16_t connHandle)
{
	AppCCB_t *pCcb = appCb.ccb;

	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->connHandle == connHandle && pCcb->inUse){
			return pCcb;
		}
	}
	return NULL;
}

void APP_MsgHandler(MdcEventMask_t event, MdcMsgHdr_t *pMsg)
{
	static uint8_t keyScanCnt = 0;

	if(pMsg)
	{
		switch(pMsg->event)
		{
		case APP_EVT_LED_BLINK:
			BSP_LedToggle(LED_PIN_BLUE);
			MDC_TimerStartMs(&appCb.timer, APP_LED_BLINK_PERIOD);
			break;

		case APP_EVT_NOTIFY:
		{
		#if APP_NOTIFY_EN
			AppCCB_t *pCCB =  &appCb.ccb[0];
			if(pCCB->inUse){
				if(pCCB->battNotifyEn){
					BAS_UpdBattLevel(pCCB->connHandle);
				}
			}
			MDC_TimerStartMs(&appCb.notifyTimer, APP_NOTIFY_INTERVAL_MS);
		#endif
			break;
		}
		case APP_EVT_DISCONN:
			break;

		case APP_EVT_PHY_UPD:
		{
			//APP_TRACK_WRN("Start PHY Upd...\r\n");

			uint16_t connHandle = pMsg->param;
			UNUSED(connHandle);

			//HCI_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_1M_BIT, LL_PHYS_LE_1M_BIT, LL_PHY_OPTIONS_NONE);
			//HCI_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_2M_BIT, LL_PHYS_LE_2M_BIT, LL_PHY_OPTIONS_NONE);
			//HCI_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_CODED_BIT, LL_PHYS_LE_CODED_BIT, LL_PHY_OPTIONS_S8_PREFERRED);
			//HCI_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_CODED_BIT, LL_PHYS_LE_CODED_BIT, LL_PHY_OPTIONS_S2_PREFERRED);
			//HCI_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_1M_BIT|LL_PHYS_LE_2M_BIT, LL_PHYS_LE_1M_BIT|LL_PHYS_LE_2M_BIT, LL_PHY_OPTIONS_NONE);
			break;
		}
		case APP_EVT_DLE_UPD:
		{
			//APP_TRACK_WRN("Start DLE Upd...\r\n");

			uint16_t connHandle = pMsg->param;
			UNUSED(connHandle);

			//HCI_LE_SetDataLen(connHandle, llCfg.maxAclLen, LL_MAX_TX_RX_TIME_1M(llCfg.maxAclLen));
			break;
		}
		case APP_EVT_CONN_UPD:
		{
		#if APP_PM_EN
			//APP_TRACK_WRN("Start Connection Upd...\r\n");
			L2C_SlvConnUpdateReq(pMsg->param, CONN_INTR_10_MS, CONN_INTR_10_MS, 99, 400);
			//L2C_SlvConnUpdateReq(pMsg->param, CONN_INTR_1S, CONN_INTR_1S, 0, 400);
			//L2C_SlvConnUpdateReq(pMsg->param, CONN_INTR_100_MS, CONN_INTR_100_MS, 0, 400);
		#endif
			break;
		}
	#if APP_KEY_EN
		case APP_EVT_KEY_SCAN:
		{
			App_KeyTaskStart();

			if(keyScanCnt++ < 5){
				App_KeyScanTimerStart();
			}
			else{
				keyScanCnt = 0;
			}
			break;
		}
	#endif
		default:
			break;
		}
	}

	if(event)
	{
	#if APP_KEY_EN
		if(event & APP_EVT_KEY_SCAN){
			//App_KeyTaskStart();
			App_KeyScanTimerStart();
			keyScanCnt = 0;
		}
	#endif
	}
}

void App_SendKeyReport(uint16_t attHandle, uint8_t *pData, uint32_t len)
{
	AppCCB_t *pCcb = appCb.ccb;
	for(int i=0; i<APP_MAX_CONN_NUM; i++, pCcb++)
	{
		if(pCcb->inUse){
			ATTS_ValueNotify(pCcb->connHandle, attHandle, pData, len);
		}
	}
}

void APP_DisconnectAll(void)
{
	AppCCB_t *pCcb = appCb.ccb;
	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->inUse){
			DM_Disconnect(pCcb->connHandle, HCI_ERR_REMOTE_USER_TERM_CONN);
		}
	}
}

/******************************************************************************/
/* Key                                                                        */
/******************************************************************************/
uint8_t sendDataEnable = 0;

#if APP_KEY_EN
static uint8_t  keyPressed  = false;
static uint8_t  keyValid = false;

void App_KeyScanTimerStart(void)
{
	MDC_TimerStartMs(&appCb.keyScanTimer, 4);
}

void App_BtnEvtCb(void)
{
	MDC_SendEvent(appCb.handlerId, APP_EVT_KEY_SCAN);
}

void App_KeyInit(void)
{
	Btn_Init();

#if CFG_BTN_IT_MODE_EN
	Btn_RegisterEvtHandler(App_BtnEvtCb);
#endif
}

void App_KeyChangeHandler(void)
{
	extern uint16_t A_connHandle;
	BtnData_t *pBtnData = Btn_GetData();

	uint16_t consumerKey = 0;
	keyPressed = true;

	switch(pBtnData->cnt)
	{
	case 0:
		if(keyValid){
			keyValid = false;
			App_SendKeyReport(HID_INPUT_REPORT_2_HDL, (uint8_t*)&consumerKey, 2);
		}
		keyPressed = false;
		break;

	case 1:
		if(pBtnData->code[0] == BTN_1){
			consumerKey = VOL_DN;
			APP_TRACK_INFO("Vol- \r\n");
		}
		else if(pBtnData->code[0] == BTN_2){
			consumerKey = VOL_UP;
			APP_TRACK_INFO("Vol+ \r\n");
		}
		else if(pBtnData->code[0] == BTN_3){
			consumerKey = MUTE;
			APP_TRACK_INFO("Mute+ \r\n");

			sendDataEnable = !sendDataEnable;
			APP_TRACK_INFO("Slave Send Data: %s\r\n", sendDataEnable ? "Enable":"Disable");
		}
		if(consumerKey){
			keyValid = true;
			App_SendKeyReport(HID_INPUT_REPORT_2_HDL, (uint8_t*)&consumerKey, 2);
		}
		break;

	case 2:
		break;

	default:
		break;
	}
}

void App_KeyTaskStart(void)
{
#if !CFG_BTN_IT_MODE_EN
	static uint32_t keyScanTick = 0;
	if(!IsTimeExpired(keyScanTick, 20 * 1000)){  //keyScan interval: 20mS
		return;
	}
	keyScanTick = GetCurSysTick();
#endif

	bool_t keyChanged = Btn_Scan();
	if(keyChanged)
	{
		App_KeyChangeHandler();
	}
}
#endif

/******************************************************************************/
/* Event Call-back                                                            */
/******************************************************************************/
uint32_t A_createConnStart = 0;
uint16_t A_connHandle = 0xffff;
uint32_t sendDataTick = 0;
uint32_t A_connCnt = 0;

void DM_EvtHandler(DmEvt_t *pEvt)
{
	switch(pEvt->hdr.event)
	{
	case DM_CONN_OPEN_IND:
		APP_TRACK_INFO("****** Connection Cmpl ******\r\n"
				"\t-connHandle:%d\r\n"
				"\t-interval:%d us\r\n"
				"\t-latency:%d\r\n"
				"\t-to:%d ms\r\n"
				"\t-peer_addr(at:%d): ",
				pEvt->leConnCmpl.handle,
				pEvt->leConnCmpl.connInterval * UNIT_1_25_MS,
				pEvt->leConnCmpl.connLatency,
				pEvt->leConnCmpl.supTimeout * 10,
				pEvt->leConnCmpl.peerAddrType);
		TRACK_DATA((uint8_t*)&pEvt->leConnCmpl.peerAddr, 6);

	#if !APP_PM_EN
		BSP_LedOn(LED_PIN_RED);
		//BSP_LedOff(LED_WHITE_PIN);//MIC failed flag
		//BSP_LedOff(LED_GREEN_PIN);//timeout flag
	#endif
		A_createConnStart = GetCurSysTick()|1;
		A_connHandle = pEvt->leConnCmpl.handle;
		sendDataTick = 0;
		A_rx_pkt_cnt = 0;
		A_tx_pkt_cnt = 0;

		APP_AllocCcb(pEvt->leConnCmpl.handle, pEvt->leConnCmpl.role);

		A_connCnt++;
		if(A_connCnt < APP_MAX_SLV_NUM){
			DM_LE_SetAdvEnable(ENABLE);
		}
		break;

	case DM_CONN_CLOSE_IND:
		APP_TRACK_INFO("Disconnection Cmpl:\r\n\t-connHandle: 0x%02x\r\n\t-reason:0x%02x\r\n",
				        pEvt->disconnectCmpl.handle, pEvt->disconnectCmpl.reason);

	#if !APP_PM_EN
		BSP_LedOff(LED_PIN_RED);
		if(pEvt->disconnectCmpl.reason == HCI_ERR_CONN_TERM_MIC_FAILURE){//mic failed
			//BSP_LedOn(LED_WHITE_PIN);
		}
		else if(pEvt->disconnectCmpl.reason == HCI_ERR_CONN_TIMEOUT){//timeout
			//BSP_LedOn(LED_GREEN_PIN);
		}
	#endif
		A_createConnStart = 0;
		A_connHandle = 0xffff; // invalid connection handle
		sendDataTick = 0;

		APP_ReleaseCcbByHandle(pEvt->disconnectCmpl.handle);

		if(A_connCnt){
			A_connCnt--;
		}
		if(A_connCnt < APP_MAX_SLV_NUM){
			DM_LE_SetAdvEnable(ENABLE);
		}
		break;

	case DM_CONN_CH_SEL_IND:
		APP_TRACK_INFO("Chn Select cmpl\r\n\t-CSA: %d\r\n", pEvt->leChnSelInd.chSelAlg);
		break;

	case DM_CONN_PHY_UPD_IND:
		APP_TRACK_INFO("Phy upd cmpl\r\n\t-Phy: %d\r\n", pEvt->lePhyUpdCmpl.txPhy);
		break;

	case DM_CONN_UPD_IND:
		APP_TRACK_INFO("Conn Upd Cmpl:\r\n\t-intvl:%d us\r\n\t-latency:%d\r\n\t-to:%d ms\r\n",
				       pEvt->leConnUpdateCmpl.connInterval *UNIT_1_25_MS,
					   pEvt->leConnUpdateCmpl.connLatency,
				       pEvt->leConnUpdateCmpl.supTimeout * 10);
		break;

	case DM_CONN_DATA_LEN_CHANGE_IND:
		APP_TRACK_INFO("LE data length changed\r\n\t-max tx: %d B\r\n\t-max rx: %d B\r\n",
				       pEvt->leDataLenChange.maxTxOctets, pEvt->leDataLenChange.maxRxOctets);
		break;

	case DM_SMP_PAIR_END_IND:
		if(pEvt->smpPairEndInd.status){
			APP_TRACK_INFO("Pair failed...connHandle: 0x%02x\r\n", pEvt->smpPairEndInd.connHandle);
		}
		else{
			APP_TRACK_INFO("Pair successfully...connHandle: 0x%02x\r\n", pEvt->smpPairEndInd.connHandle);
			AppCCB_t *pCcb = APP_GetConnCb(pEvt->smpPairEndInd.connHandle);
			if(pCcb){
				pCcb->isSmpCmpl = true;
			}
		}
		break;

	case DM_SMP_PIN_CODE_IND:
		APP_TRACK_INFO("connHandle:%d, Pin_Code:%06d\r\n",pEvt->smpPinCodeInd.connHandle, pEvt->smpPinCodeInd.pinCode);
		break;

	case DM_SMP_PIN_CODE_REQ:
	{
		uint32_t pinCode = 123456;
		SMP_SetPinCode(pEvt->smpPinCodeReq.connHandle, pinCode);
		APP_TRACK_INFO("%6d\r\n", pinCode);
		break;
	}

	default:
		break;
	}
}

void ATT_EvtHandler(AttEvt_t *pEvt)
{
	switch(pEvt->hdr.event)
	{
	case ATT_MTU_UPDATE_IND:
		APP_TRACK_INFO("MTU Upd Cmpl\r\n\t-MTU:%d B\r\n", pEvt->mtu);
		break;

	case ATTS_CCC_STATE_IND:
	{
	#if APP_NOTIFY_EN
		uint16_t connHandle = pEvt->handle;
		uint8_t *p = pEvt->pValue;
		uint16_t attHandle = 0;
		uint16_t value = 0;
		BSTREAM_TO_UINT16(attHandle, p);
		BSTREAM_TO_UINT16(value, p);
		APP_TRACK_INFO("CCC(handle:%d):0x%04x\n", attHandle, value);

		if(attHandle == BATT_LVL_CH_CCC_HDL && value == NOTIFY_ENABLE){
			AppCCB_t *pCcb = APP_GetConnCb(connHandle);
			if(pCcb){
				pCcb->battNotifyEn = value?1:0;
				if(pCcb->battNotifyEn){
					MDC_TimerStartMs(&appCb.notifyTimer, APP_NOTIFY_INTERVAL_MS);
				}
				else{
					MDC_TimerStop(&appCb.notifyTimer);
				}
			}
		}
	#endif
		break;
	}
	case ATTS_HANDLE_VALUE_CNF:
		APP_TRACK_INFO("ATT Handle Value Confirm\r\n");
		break;

	default:
		break;
	}
}

void APP_Init(void)
{
	/* APP manager initialization. */
	appCb.handlerId = MDC_AddMsgHandler(APP_MsgHandler);

#if APP_LED_EN
	/* BSP initialization. */
	BSP_InitAllLed();

	/* LED Timer initialization. */
	MDC_TimerConfigEx(&appCb.timer, appCb.handlerId, APP_EVT_LED_BLINK, 0, 0);
	MDC_TimerStartMs(&appCb.timer, APP_LED_BLINK_PERIOD);
#endif

#if APP_NOTIFY_EN
	MDC_TimerConfigEx(&appCb.notifyTimer, appCb.handlerId, APP_EVT_NOTIFY, 0, 0);
#endif

#if APP_KEY_EN
	/* keyboard initialization. */
	App_KeyInit();

	/* Key Scan Timer initialization. */
	MDC_TimerConfigEx(&appCb.keyScanTimer, appCb.handlerId, APP_EVT_KEY_SCAN, 0, 0);
#endif
}

void APP_Start(void)
{
#if APP_KEY_EN && !CFG_BTN_IT_MODE_EN
	App_KeyTaskStart();
#endif
}

void Setup(void)
{
	/* memory initialization. */
	lg_mem_init();
	HeapInit((uint8_t *)&heap[0], APP_HEAP_MEM_SIZE);

	/* kv database initialization. */
	kv_init();

	/* Buffer pool alloc. */
	pool_desc_t pools[] ={
		{16,     8 },
		{32,     4 },
		{80,     8 },
	    {280,    4 },
	};
	PoolBufInit(pools, COUNTOF(pools));

	/* MDC system initialization. */
	MDC_Init();

	/* MDC Timer initialization. */
	MDC_TimerInit();

	/* LL initialization. */
	LL_Init(&llCfg);
	LL_SlvInit();
	BB_SetTxPower(CFG_TX_POWER_LVL);

	/* Set MAC address. */
	uint8_t bdAddr[6] = {0x66, 0x66, 0x66, 0x66, 0x00, 0xC2};
	GetConfigParam(CFG_PARAM_MAC_ADDR, bdAddr, 6);
	LL_SetBdAddr(bdAddr, BD_ADDR_LEN);

	APP_TRACK_INFO("BD_ADDR:");
	APP_TRACK_DATA(bdAddr, BD_ADDR_LEN);

#if APP_ADV_LOCAL_ADDR_TYPE == LL_ADDR_RANDOM
	// Set Random Address(static random)
	uint8_t randAddr[6] = {0x66, 0x66, 0x66, 0x66, 0xff, 0xC2};
	randAddr[4] = PrandNum() & 0xff;
	DM_LE_SetRandomAddr(randAddr, 6);

	APP_TRACK_INFO("RND_ADDR:");
	APP_TRACK_DATA(randAddr, BD_ADDR_LEN);
#endif

	/* Host initialization. */
	hostCfg.numMaxConn = APP_MAX_CONN_NUM;

	/* HCI initialization. */
	HCI_Init();
	HCI_SetMaxAclLen(247 + 4);/*!< Set L2CAP payload size= 104B ==> ATT MTU Size = 100. */
	HCI_LE_SetDefDataLen(llCfg.maxAclLen, LL_MAX_TX_RX_TIME_1M(llCfg.maxAclLen));

	/* DM initialization. */
	DM_Init();
	DM_AdvInit();
	DM_SecInit();
	DM_EvtRegister(DM_EvtHandler);
#if 0
	DM_SecSetInitiateSecEnable(false);
	DM_SecSetIoCap(IO_CAP_DISPLAY_ONLY);
	DM_SecSetAuth(SMP_AUTH_MITM_FLAG);
	DM_SecSetDefPinCode(123456, ENABLE);
	//DM_SecSetAuth(SMP_AUTH_BOND_FLAG | SMP_AUTH_MITM_FLAG);
	//DM_SecSetKeyDist(SMP_ENCKEY);
#else
	DM_SecSetInitiateSecEnable(true);
#endif

	/* L2CAP initialization. */
	L2C_Init();
	L2C_SlvInit();

	/* ATT initialization. */
	ATT_Init();
	ATT_EvtRegister(ATT_EvtHandler);
	ATTS_Init();
	ATTS_CccRegister(&ServerCccSets[0], CCC_IDX_MAX, NULL);

	/* Register GATT services */
	ATTS_AddAttGroup(&gapGrp);
	ATTS_AddAttGroup(&gattGrp);
	ATTS_AddAttGroup(&battGrp);
	ATTS_AddAttGroup(&hidGrp);

	GAP_SetDeviceName("PHID");

	/* SMP initialization. */
#if 0
	SMP_NonInit();
#else
	SMP_Init();
	SMP_RespInit();
#endif

	/* ADV initialization. */
	DmAdvParam_t advParam = {
		.minInterval  = APP_ADV_INTR_MIN,
		.maxInterval  = APP_ADV_INTR_MAX,
		.advType      = APP_ADV_EVT_TYPE,
		.ownAddrType  = APP_ADV_LOCAL_ADDR_TYPE,
		.peerAddrType = APP_ADV_PEER_ADDR_TYPE,
		.pPeerAddr    = NULL,
		.chnMap       = APP_ADV_CHN,
		.filter       = APP_ADV_FILT,
	};
	DM_LE_SetAdvParam(&advParam);

	uint8_t advData[] = {
		/*!< Flag type.  */
		0x02, 0x01, 0x06,

		/*!< short name. */
		0x05, 0x08, 'P','H','I','D',

		/*!< 384, Generic Remote Control, Generic category */
		0x03, 0x19, 0x80, 0x01,

		 /*!< service UUID */
		0x03, 0x02, 0x12, 0x18,
	};
	uint8_t scanRsp[] = {
		/*!< complete name. */
		0x05, 0x09, 'P','H','O','D',
	};
	DM_LE_SetAdvData(advData, sizeof(advData));
	DM_LE_SetScanRspData(scanRsp, sizeof(scanRsp));
	DM_LE_SetAdvEnable(ENABLE);

#if APP_PM_EN
	LL_PmInit(NULL, ENABLE);
#endif

	/* Application initialization. */
	APP_Init();
}

#if 1
uint8_t data1[] = {
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,//80

	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,//80

	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,//80

	0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
};

void GW_RxDataHandler(uint16_t connHandle, uint8_t *pdata, uint32_t len)
{
	uint8_t *p = pdata;
	uint32_t rxCnt = p[3]<<24 | p[2]<<16 | p[1]<<8 | p[0];
	if(A_rx_pkt_cnt != rxCnt){
		APP_TRACK_INFO("RX Data error(len:%d, rxCnt:%d) \r\n", len, A_rx_pkt_cnt);
		APP_TRACK_DATA(p, len);
		return;
	}

	uint8_t token = 0xAA;
	for(int i=4; i<len; i++)
	{
		if(p[i] != token){
			APP_TRACK_INFO("RX Data error(len:%d, rxCnt:%d) \r\n", len, A_rx_pkt_cnt);
			APP_TRACK_DATA(p, len);
			return;
		}
	}

	//APP_TRACK_INFO("RX Data (len:%d, rxCnt:%d) \r\n", len, A_rx_pkt_cnt);
	A_rx_pkt_cnt++;
}
#endif

uint32_t A_loop=0;
void Loop(void)
{
	A_loop++;

#ifndef OS_EN
	MDC_Start();
#endif

	APP_Start();

#if !APP_PM_EN
	if(A_createConnStart && IsTimeExpired(A_createConnStart, 6000*1000))
	{
		A_createConnStart = 0;
		sendDataTick = GetCurSysTick()|1;
	}
#endif

#if 0
	if( sendDataTick && IsTimeExpired(sendDataTick, 10*1000))
	{
		sendDataTick = GetCurSysTick()|1;

		AppCCB_t *pCcb = appCb.ccb;
		for(int i=0; i<APP_MAX_CONN_NUM; i++, pCcb++)
		{
			if(pCcb->inUse && LL_GetFreeTxBufNum(pCcb->connHandle) > 2)
			{
				uint8_t *p = data1;
				UINT32_TO_BSTREAM(p, A_tx_pkt_cnt);
				p = data1;

				if(GW_SendData(pCcb->connHandle, p, sizeof(data1)) == ATT_SUCCESS){
					//p[0]++;

					APP_TRACK_INFO("Tx Packet Count:%u\r\n", A_tx_pkt_cnt);
					A_tx_pkt_cnt++;
				}
			}
		}
	}
#endif

#if APP_PM_EN
	/* PM Handler */
	PM_SleepStart();
#endif
}

